iT邦幫忙

2025 iThome 鐵人賽

DAY 9
0

今日內容:public, private, protective、什麼是套件(package)、反向域名命名法 (Reverse Domain Name)、package的使用方式

public, private, protective

public, private 和 protected 是存取修飾子 (Access Modifiers),他們用於控制class、attributes、methods或constructor可以被誰存取,是用來定義 可見性 (visibility) 的規則。

  1. public (公開)

    • public 是最寬鬆的存取層級。public的成員可以在任何地方被存取,且public class也可以有private的內容(class本身可以在任何地方被存取、可是內部資料只能在class內部存取,也就是上方使用getter&setter的原因)。

    • 使用時機:當你希望一個class或method或attribute對所有其他的class都可見時
      例如,一個名為 Utility的public class,或一個公開的method 例如 public void countSomething()

    • 例子:
      public class Car:Car 可以在任何套件 (package) 中被使用。
      public String color:Car object的 color 屬性可以從任何地方直接存取與修改,例如 myCar.color = "Red";。

  2. private (私有)

    • private 是最嚴格的存取層級。private的成員只能在它所屬的class內部被存取,用以確保資料安全性。

    • 使用時機:當你希望封裝 (encapsulation) class的內部實作細節,避免外部的直接存取和修改
      這是物件導向設計(OOP)的最佳實踐與目的,用來保護資料。

    • 例子:
      private int speed:Car object的 speed 屬性只能在 Car class內部被使用。外部無法直接存取,例如 myCar.speed = 10 會報錯。
      通常會提供 public 的 getter 和 setter methods來間接存取這些 private 屬性,例如 public int getSpeed() 和 public void setSpeed(int speed)。這能讓你對資料的存取進行控制和驗證。

  3. protected (受保護)

    • protected 是介於 public 和 private 之間的存取層級。protected的成員只能在以下兩個地方被存取:

      • 同一個套件 (package) 內的任何類別。
      • 不同套件的inheritance關係。即使和parent class不在同一個套件,subclass仍然可以存取parent class的 protected 成員。
    • 使用時機: 當你希望一個attribute或methods對同一個繼承關係的class開放,但又對外部封閉時。這讓subclass可以繼承並使用parent class的內部細節,同時又保護了這些細節不被不相關的類別隨意存取。

    • 例子:
      protected int wheelCount:如果 Car 有一個 protected 的 wheelCount,在同一個套件裡的 Truck 也可以存取它。而一個不同套件的subclass,例如 SportsCar extends Car,也可以存取這個屬性。

  4. 預設存取層級

    • 如果你沒有指定任何修飾子,它會使用預設存取層級 (Default Access Level),也稱為套件私有 (Package-Private)。這意味著該成員只能在同一個套件內被存取。

有關class的補充

  • public class:如果一個class是 public 的,它的檔案名稱必須和類別名稱相同(例如:public class MyClass 這個class 必須存在於 MyClass.java 檔案中)。這是硬性規定,一個 .java file 中最多只能有一個 public class。

  • 預設 (無修飾子) class:如果一個class沒有任何存取修飾子,它就是預設的套件私有 (Package-Private)。這表示這個類別只能在同一個套件 (package) 內的類別中被存取,無法被其他套件的類別使用。宣告成預設的class通常是用來作為輔助類別(Helper class)


什麼是套件(package)

想像一下你的程式碼就是一座巨大的公寓大樓。這座大樓裡住著各種各樣的人,也就是你的class和interface。
而package就是這座「公寓大樓」的「樓層」

每一個樓層都有一個自己的名稱,例如「com.example.game」或「com.example.util」。
每個class就是一個住戶,屬於同一個樓層的住戶通常是鄰居,彼此之間會有密切關係,功能上也經常互相依賴。
你不能把一個住戶同時放在兩個不同的樓層。同樣地,一個 Java 檔案 (.java file)也只能屬於一個 package。

每個住戶都有一個獨特的名稱,例如 Player 或 Monster。
即使其他大樓(其他專案)也有一個住戶叫做 Player,只要他們在不同的大樓裡,就不會搞混。同樣地,com.example.game.Player 和 com.othercompany.engine.Player 是兩個完全不同的類別。

為什麼要有「樓層」?

有了 package (樓層) 的概念,我們就能解決兩個重要的問題:

  1. 管理與組織

    如果沒有樓層,所有住戶都擠在一個地方,找人就會非常困難。
    package 讓你可以把功能相似的class放在一起。例如,所有遊戲角色的類別都放在 game 樓層,所有工具程式都放在 util 樓層,使得程式碼結構清晰,更容易找到需要的東西。

  2. 避免名稱衝突

    世界上有很多人都叫「小明」。但只要加上他們住的地址,就不會搞錯。例如,「台北市忠孝東路三段的小明」和「台中市文心路二段的小明」是不同的兩個人。
    同樣地,package 提供了這樣的命名屬性。即使你的class名稱和其他的重複了,只要它們在不同的 package 中,就不會有問題。

package 和存取權限的關係

  1. public (公開):

    這就像一個住在樓層頂樓的住戶,或者想成社區的健身房,他的大門對所有人開放,任何人都可以隨時去拜訪他。

  2. private (私有):

    這就像一個普通住戶的房間,只有他自己(同個class)才能進去。其他人(包括同個樓層的鄰居)都不能進入。

  3. protected (受保護):

    這就像一個住戶的公寓大門只對同樓層的鄰居,以及繼承他的subclass開放。其他人即使知道他住在哪層樓,也進不去他的公寓。

  4. package-private (預設):

    這就像一個住戶的公寓大門只對同樓層的鄰居開放。只有住在同一層樓的人可以自由地進出,而住在其他樓層的人則不能。


反向域名命名法 (Reverse Domain Name)

由於我目前還沒學會製作專案,因此我src資料夾下的 Main.java 和 Animal.java 這種檔案,其實是在沒有明確指定套件的情況下,它們都被放在一個預設的、無名的套件 (default package) 裡。

com.example.game 的真正意義

這些資料夾名稱只是為了標識你的程式碼。
以上述舉例中的 com.example.game 進行解釋

  • com:代表 company (公司),這是標準約定的第一層資料夾。

  • example:代表你的公司名稱或專案名稱,例如 google、apple 或 myproject。

  • game:代表你這個專案或功能模組的名稱,例如 database、ui 或 network。

為什麼要這麼做?

假設你正在開發一個遊戲,而你公司的網域名稱(domain name)是 example.com。根據 Java 的慣例,你的程式碼套件名稱就會以 com.example (reversed) 開頭。

當你的專案成長,你會有越來越多的程式碼檔案。這時,你可以把不同功能的類別放在不同的子套件中:

  • src/com/example/game/characters/Player.java:存放遊戲角色的類別。

  • src/com/example/game/utils/MathUtils.java:存放遊戲中用到的數學工具類別。

  • src/com/example/game/Main.java:存放遊戲的啟動程式。


package的使用方式

只要掌握兩個步驟:宣告 (declare) 和匯入 (import)。

第一步:宣告 package (Declaration)

當你開始寫一個新的 Java 類別時,你需要在檔案的最頂部宣告它屬於哪一個套件。

舉例來說,我們來建立一個專案,包含兩個不同的套件:utils (工具) 和 main (主程式)。

  1. 創建資料夾結構
    在 src 資料夾下,創建以下目錄結構:

     src/
     └── com/
         └── myproject/
             ├── utils/
             │   └── MathUtils.java  // 存放數學工具
             └── main/
                 └── Main.java       // 存放主程式
    
  2. 宣告 utils 這個 package
    在 src/com/myproject/utils/MathUtils.java 檔案中,在class宣告前加上一行程式碼:

    package com.myproject.utils; // 宣告此類別屬於 com.myproject.utils package
    
    public class MathUtils {
        public static int add(int a, int b) {
            return a + b;
        }
    }
    
  3. 宣告 main 的 package
    同樣地,在 src/com/myproject/main/Main.java 檔案中,宣告它屬於 com.myproject.main 套件:

     package com.myproject.main; // 宣告此類別屬於 com.myproject.main 套件
    
     // ... 後面再介紹如何使用 MathUtils 這個 class
    

第二步:匯入 package (Import)

當你需要在一個class中使用其他package中的class時,你必須先匯入 (import) 它。

回到我們的 Main.java 檔案,現在我們想使用 MathUtils 這個class來執行加法運算。
在 Main.java 檔案中,在 package 宣告之後、宣告Main class之前,加入 import 語句:

package com.myproject.main;

// 匯入 com.myproject.utils package 中的 MathUtils class
import com.myproject.utils.MathUtils;

public class Main {
    public static void main(String[] args) {
        // 現在就可以直接使用 MathUtils 這個class的內容了
        int sum = MathUtils.add(5, 3);
        System.out.println("The sum is: " + sum);
    }
}

總結與撇步

  • 宣告 (Declaration):用 package 關鍵字來告訴 Java ,這個class的「地址」。這是所有非預設套件 (default package) 的class都必須做的第一件事。

  • 匯入 (Import):用 import 關鍵字來告訴 Java 編譯器你要使用哪些其他package的class。

  • 撇步

    • import com.myproject.utils.*;:如果你想 import 一個package中的所有public class,可以使用星號 *。但通常不推薦,因為它會使你的程式碼可讀性降低,並且可能會造成命名衝突。
    • 使用整合開發環境 (IDE):像 VS Code、IntelliJ 或 Eclipse 這樣的 IDE,會自動幫你處理這些匯入語句。你通常只需要寫下類別名稱,IDE 會自動提示你並完成匯入。

結語

今天是為自己解決Java問題的一天,在昨天把學到的知識整理過後,發現自己不理解Access Modifier的詳細運作模式,詢問Gemini後又問到有關Package的問題,所以今天一次把這些問題處理掉。
今天也是快樂學習的一天,明天繼續!/images/emoticon/emoticon47.gif


上一篇
Day 8:Java OOP基礎(三)
下一篇
Day 10:Java基本語法(五)
系列文
30天從基礎學起Java,直到做出我的第一個遊戲21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言